import { RootContext } from '@/App.context'
import { makeAutoObservable } from 'mobx'
import { Asset, DesignMethod } from 'wts/dist/schema/Conception'
import { Graph } from '@antv/x6'
import {
  addAssetInCanvas,
  addEdgeInCanvas,
  addMethodInCanvas,
} from '@/components/MainGraph/utils'
import { TextAsset } from '@/components/graph/assets/TextAsset/TextAsset'
import React from 'react'
import { RequirementsSummaryMethod } from '@/components/graph/methods/RequirementsSummaryMethod/RequirementsSummaryMethod'
import WtsStore from '@/store/wtsStore'
import { ImageAsset } from '@/components/graph/assets/ImageAsset/ImageAsset'

interface IMetaMethod {
  methodID: string
  methodName: string
  methodType: string // generate_design_goals / text_to_image...
  wtsMethod: DesignMethod
}

interface IMetaAsset {
  assetID: string | string[]
  assetName: string | string[]
  assetType: string
  wtsAsset: Asset | Asset[]
  assetData: any
}

export type UUID = string
interface IMyCard {
  cardID: UUID
}

// TODO: add base type
type HistoryActionAddEdge = {
  action: 'addEdge'
  fromID: string
  toID: string
}
const supportedCardTypes = [
  'TextAsset',
  'ImageAsset',
  'RequirementsSummaryMethod',
] as const
type SupportedCardType = typeof supportedCardTypes[number]
type HistoryActionAddCard = {
  action: 'addCard'
  cardID: string
  cardType: SupportedCardType
  position: [number, number] // TODO
}
type HistoryAction = HistoryActionAddEdge | HistoryActionAddCard
type Serialized = {
  cardContext: object
  cardContextArray: ICardContext[]
  history: HistoryAction[]
}
export interface ICardContext {
  position: [number, number]
}
export interface ITextAssetCardContext extends ICardContext {
  assetID?: UUID
}
export interface IRequirementSummaryCardContext extends ICardContext {
  inputTextAssetCardID?: UUID
}
export interface IImage2ImageCardContext extends ICardContext {
  inputPositivePromptAssetCardID?: UUID
  inputNegativePromptAssetCardID?: UUID
  inputReferenceImageAssetCardID?: UUID
  inputMaskImageAssetCardID?: UUID
}

// const serializeMap = <U, V>(map: Map<U, V>) =>
//   JSON.stringify(Array.from(map.entries()))
// const deserializeMap = (mapSerialized: string) =>
//   new Map(JSON.parse(mapSerialized))

// 存储 asset、method
export default class MetaStore {
  public context: RootContext

  public cards: IMyCard[] = []
  public methods: IMetaMethod[] = []
  public assets: IMetaAsset[] = []
  public cardContext: object = {}
  public cardContextArray: ICardContext[] = []
  private history: HistoryAction[] = []

  constructor(context: RootContext) {
    this.context = context
    makeAutoObservable(this, {}, { autoBind: true })
  }

  onAddEdge = (fromCardID: UUID, toCardID: UUID) => {
    const index = this.cards.findIndex(({ cardID }) => cardID === toCardID)
    console.log('onaddedge', index)
    this.cardContext[toCardID] = this.cardContextArray[index] = {
      ...this.cardContext[toCardID],
      inputTextAssetCardID: [fromCardID],
    }
    this.history.push({ action: 'addEdge', fromID: fromCardID, toID: toCardID })
  }

  // onRemoveEdge = () => {} TODO

  addCard = (cardID: UUID) => {
    this.cards.push({
      cardID,
    })
    return this.cards.length - 1
  }

  registerNonAssetCardAsAsset = async (
    cardID: UUID,
    assetData: unknown,
    assetType = 'text'
  ) => {
    const wtsStore = this.context.store.wts
    const assetID = await wtsStore.operator.add_asset(assetData, '', assetType)
    this.cardContext[cardID] = { ...this.cardContext[cardID], assetID }
  }

  addTextAssetCard =
    (graph: Graph) =>
    (
      useHistory: boolean,
      cardID: UUID,
      position: [number, number],
      text = ''
    ) => {
      this.addCard(cardID)
      addAssetInCanvas({
        graph: graph,
        cardID: cardID,
        component: ({ node, graph }) => (
          <TextAsset
            node={node}
            graph={graph}
            text={text}
            cardID={cardID}
            isEditable={true}
          />
        ),
        position,
      })
      this.cardContext[cardID] = { ...this.cardContext[cardID], position }
      console.log('cardcontext', this.cardContext)
      if (useHistory) {
        this.history.push({
          action: 'addCard',
          cardType: 'ImageAsset',
          cardID,
          position,
        })
      }
    }

  addImageAssetCard =
    (graph: Graph) =>
    (useHistory: boolean, cardID: UUID, position: [number, number]) => {
      this.addCard(cardID)
      addAssetInCanvas({
        graph: graph,
        cardID: cardID,
        component: ({ node, graph }) => (
          <ImageAsset
            node={node}
            graph={graph}
            cardID={cardID}
            isEditable={true}
          />
        ),
        position,
      })
      this.cardContext[cardID] = { ...this.cardContext[cardID], position }
      console.log('cardcontext', this.cardContext)
      if (useHistory) {
        this.history.push({
          action: 'addCard',
          cardType: 'TextAsset',
          cardID,
          position,
        })
      }
    }

  addRequirementSummaryMethod =
    (graph: Graph) =>
    (
      useHistory: boolean,
      cardID: UUID,
      position: [number, number],
      inputTextAssetCardID = ''
    ) => {
      const cardIndex = this.addCard(cardID)
      addMethodInCanvas({
        graph,
        cardID,
        component: ({ node, graph }) => (
          <RequirementsSummaryMethod
            node={node}
            graph={graph}
            cardID={cardID}
            cardIndex={cardIndex}
          />
        ),
        position,
      })
      this.cardContext[cardID] = { ...this.cardContext[cardID], position }
      if (useHistory) {
        this.history.push({
          action: 'addCard',
          cardType: 'RequirementsSummaryMethod',
          cardID,
          position,
        })
      }
    }

  save = async (wtsStore: WtsStore) => {
    const serialized: Serialized = {
      cardContext: this.cardContext,
      cardContextArray: this.cardContextArray,
      history: this.history,
    }
    console.log('serialized', serialized)
    await wtsStore.operator.save_canvas(serialized)
  }

  load = async (wtsStore: WtsStore, graph: Graph) => {
    const serialized = (await wtsStore.operator.load_canvas()) as Serialized
    const { history, cardContextArray, cardContext } = serialized
    this.cards = []
    this.cardContextArray = cardContextArray
    this.cardContext = cardContext
    console.log('load', history, cardContext, cardContextArray)
    for (const historyItem of history) {
      if (historyItem.action === 'addEdge') {
        const { fromID, toID } = historyItem
        addEdgeInCanvas(graph)(fromID, 0, toID, 0) // TODO
      } else if (historyItem.action === 'addCard') {
        const { cardID, cardType } = historyItem
        const position = this.cardContext[cardID].position
        if (cardType === 'TextAsset') {
          const assetID =
            (this.cardContext[cardID] as ITextAssetCardContext)?.assetID ?? '' // TODO: as -> validate
          let text = ''
          if (assetID) {
            try {
              text = await wtsStore.app.get_asset_data(assetID)
              this.addTextAssetCard(graph)(false, cardID, position, text)
            } finally {
            }
          }
        } else if (cardType === 'ImageAsset') {
          // TODO
          // const assetID =
          //     (this.cardContext[cardID] as ITextAssetCardContext)?.assetID ?? '' // TODO: as -> validate
          // let text = ''
          // if (assetID) {
          //   try {
          //     text = await wtsStore.app.get_asset_data(assetID)
          //     this.addTextAssetCard(graph)(false, cardID, position, text)
          //   } finally {
          //   }
          // }
        } else if (cardType === 'RequirementsSummaryMethod') {
          const inputTextAssetCardID = (
            this.cardContext[cardID] as IRequirementSummaryCardContext
          )?.inputTextAssetCardID // TODO: as -> validate
          this.addRequirementSummaryMethod(graph)(
            false,
            cardID,
            position,
            inputTextAssetCardID
          )
        } // TODO: make it exhaustive
      } // TODO: make it exhaustive
    }
  }
}
